package org.zoomdev.zoom.dao.impl;

import org.apache.commons.lang3.StringUtils;
import org.zoomdev.zoom.common.utils.Classes;
import org.zoomdev.zoom.dao.DaoException;
import org.zoomdev.zoom.dao.Entity;
import org.zoomdev.zoom.dao.EntityBuilder;
import org.zoomdev.zoom.dao.SqlBuilder;
import org.zoomdev.zoom.dao.adapters.DataAdapter;
import org.zoomdev.zoom.dao.adapters.RecordAdapter;
import org.zoomdev.zoom.dao.annotations.Column;
import org.zoomdev.zoom.dao.annotations.ColumnIgnore;
import org.zoomdev.zoom.dao.annotations.Join;
import org.zoomdev.zoom.dao.annotations.Link;
import org.zoomdev.zoom.dao.meta.JoinMeta;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.HashSet;

final class EntityBuilderImpl implements EntityBuilder {


    private final CachedEntityFactory factory;
    TableSource table;
    ArrayList<JoinMeta> joinMetas;
    Class<?> type;
    ArrayList<LinkPair> links = new ArrayList<>();
    HashSet<String> ignores = new HashSet<>();
    boolean dotStyle;

    EntityBuilderImpl(CachedEntityFactory factory) {
        this.factory = factory;
    }

    public static class LinkPair {
        Type[] types;
        String fieldName;
        String columnName;
        DataAdapter adapter;

        public LinkPair(String fieldName, String columnName, DataAdapter adapter) {
            this.fieldName = fieldName;
            this.columnName = columnName;
            this.adapter = adapter;
            if (adapter != null) {
                Class type = adapter.getClass();
                Type[] types = Classes.getAllParameterizedTypes(type);
                if (types == null) {
                    throw new DaoException(type + "必须是泛型类型");
                }
                if (types.length < 2) {
                    throw new DaoException(type + "必须有至少两个泛型类型");
                }
                this.types = types;
            }
        }

    }

    public boolean isIgnore(Field field) {
        String fn = field.getName();

        if (ignores.contains(fn)) {
            return true;
        }

        for (LinkPair pair : links) {
            if (pair.fieldName.equals(fn)) {
                return false;
            }
        }

        return field.isAnnotationPresent(ColumnIgnore.class);
    }

    public LinkPair getLink(Field field) {
        LinkPair p = getLink(field.getName());
        if (p != null) {
            return p;
        }

        Column column = field.getAnnotation(Column.class);
        if (column != null) {
            DataAdapter dataAdapter = null;
            if (column.adapter() != DataAdapter.class) {
                dataAdapter = (DataAdapter) Classes.newInstance(column.adapter());
            }
            return new LinkPair(field.getName(), column.value(), dataAdapter);
        }

        return null;
    }

    public LinkPair getLink(String field) {
        for (LinkPair p : links) {
            if (p.fieldName.equals(field)) {
                return p;
            }
        }
        return null;
    }

    @Override
    public EntityBuilder type(Class<?> type) {
        if (type == null) {
            throw new InvalidParameterException("type不能null");
        }
        this.type = type;
        return this;
    }

    @Override
    public EntityBuilder table(String table) {
        if (table == null) {
            throw new InvalidParameterException("table不能null");
        }
        this.table = TableSource.from(table);
        return this;
    }

    @Override
    public EntityBuilder innerJoin(String table, String on) {
        return join(SqlBuilder.Join.INNER, table, on);
    }

    @Override
    public EntityBuilder leftJoin(String table, String on) {
        return join(SqlBuilder.Join.LEFT, table, on);
    }

    @Override
    public EntityBuilder rightJoin(String table, String on) {
        return join(SqlBuilder.Join.RIGHT, table, on);
    }

    @Override
    public EntityBuilder ignore(String fieldName) {
        if (StringUtils.isEmpty(fieldName)) {
            throw new InvalidParameterException("fieldName必须有值");
        }
        ignores.add(fieldName);
        return this;
    }

    @Override
    public EntityBuilder link(String fieldName, String columnName) {
        return link(fieldName, columnName, null);
    }

    @Override
    public EntityBuilder link(String fieldName, String columnName, DataAdapter adapter) {
        links.add(new LinkPair(fieldName, columnName, adapter));
        return this;
    }

    @Override
    public EntityBuilder dotStyle() {
        dotStyle = true;
        return this;
    }

    @Override
    public Entity build() {
        if (type != null) {
            if (table == null) {
                this.table = TableSource.from(TableNameUtils.getTableName(type));
            }

            if (joinMetas == null) {
                Link link = type.getAnnotation(Link.class);
                if (link != null) {
                    Join[] joins = link.value();
                    if (joins != null && joins.length > 0) {
                        for (int i = 0; i < joins.length; ++i) {
                            Join join = joins[i];
                            join(join.type(), join.table(), join.on());
                        }
                    }
                }
            }
        }

        return factory.build(this);
    }

    public EntityBuilder join(SqlBuilder.Join type, String table, String on) {
        if (type == null) {
            throw new InvalidParameterException("type不能null");
        }
        return join(type.getValue(), table, on);
    }

    public EntityBuilder join(String type, String table, String on) {
        if (table == null) {
            throw new InvalidParameterException("table不能null");
        }
        if (on == null) {
            throw new InvalidParameterException("on不能null");
        }
        if (joinMetas == null) {
            joinMetas = new ArrayList<>();
        }
        JoinMeta joinMeta = new JoinMeta();
        joinMeta.setOn(on);
        joinMeta.setTable(TableSource.from(table));
        joinMeta.setType(type);
        joinMetas.add(joinMeta);
        return this;
    }

    public EntityBuilder adapter(RecordAdapter adapter) {
        return this;
    }

    public JoinMeta[] getJoins() {
        if (joinMetas == null) {
            return null;
        }
        return joinMetas.toArray(new JoinMeta[joinMetas.size()]);
    }

    public TableSource[] getTables() {
        if (joinMetas != null) {
            TableSource[] tables = new TableSource[joinMetas.size() + 1];
            tables[0] = this.table;
            int index = 1;
            for (JoinMeta join : joinMetas) {
                tables[index++] = join.getTable();
            }
            return tables;
        } else {
            return new TableSource[]{table};
        }

    }


}
